package com.intellif.mopzing.lock.aop;

/**
 * @author by mozping
 * @Classname RlockRepeatSubmitAspect
 * @Description TODO
 * @Date 2019/9/6 11:34
 */

import com.intellif.mopzing.lock.annotation.RlockRepeatSubmit;
import com.intellif.mopzing.lock.enums.LockConstant;
import com.intellif.mopzing.lock.redisimpl.RedissonLockImpl;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 *
 * 防止重复提交分布式锁拦截器
 *
 * @author
 * 2019年6月18日
 *
 */
@Aspect
@Component
public class RlockRepeatSubmitAspect {

    @Resource
    private RedissonLockImpl redissonLocker;

    /***
     * 定义controller切入点拦截规则，拦截RlockRepeatSubmit注解的业务方法
     */
    @Pointcut("@annotation(xx.xxx.RlockRepeatSubmit)")
    public void pointCut() {
    }

    /**
     * AOP分布式锁拦截
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Around("pointCut()")
    public Object rlockRepeatSubmit(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取类里面的方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method targetMethod = methodSignature.getMethod();
        RlockRepeatSubmit repeatSubmit = targetMethod.getAnnotation(RlockRepeatSubmit.class);
        // 如果添加了RlockRepeatSubmit这个注解，需要添加分布式锁
        if (Objects.nonNull(repeatSubmit)) {
            // 获取参数
            Object[] args = joinPoint.getArgs();
            // 进行一些参数的处理，比如获取订单号，操作人id等
                        ......

            HttpServletRequest request = getRequest();
            if (null != request && request.getParameterMap().containsKey("accessToken")) {
                StringBuffer lockKeyBuffer = new StringBuffer();
                LockConstant lockConstant = repeatSubmit.lockConstant();
                lockKeyBuffer.append(lockConstant.getKeyPrefix());
                String accessToken = request.getParameterMap().get("accessToken")[0];// 当前用户的token
                String path = request.getServletPath();
                // 使用用户的token和请求路径作为唯一的标识，如果有orderNo，加上orderNo作为唯一标识
                lockKeyBuffer.append("RlockRepeat");
                String token =  accessToken + "." + path;
                if (null != orderNo) {
                    lockKeyBuffer.append(token.hashCode());
                    lockKeyBuffer.append(".");
                    lockKeyBuffer.append(orderNo);
                } else {
                    lockKeyBuffer.append(token);
                }
                // 公平加锁，lockTime后锁自动释放
                boolean isLocked = false;
                try {
                    isLocked = redissonLocker.fairLock(lockKeyBuffer.toString(), TimeUnit.SECONDS, lockConstant);
                    if (isLocked) { // 如果成功获取到锁就继续执行
                        // 执行进程
                        return joinPoint.proceed();
                    } else { // 未获取到锁
                        //异常处理
                                                ......
                    }
                } catch (Exception e) {
                    //异常处理
                                          ......
                } finally {
                    if (isLocked) { // 如果锁还存在，在方法执行完成后，释放锁
                        redissonLocker.unlock(lockKeyBuffer.toString());
                    }
                }
            }
        }
        return joinPoint.proceed();
    }

    public static HttpServletRequest getRequest() {
        ServletRequestAttributes ra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return ra.getRequest();
    }

}
